<?php

namespace yunj\app\admin\service\setting;

use think\facade\Db;
use yunj\app\admin\enum\auth\AuthPageOpen;
use yunj\app\admin\enum\auth\AuthType;
use yunj\app\admin\enum\route\RequestMethod;
use yunj\app\admin\service\Service;
use yunj\core\builder\YunjForm;
use yunj\core\Config;
use yunj\core\constants\BuilderConst;
use yunj\core\enum\RedisKey;
use yunj\core\exception\ResponseJsonException;

class SettingService extends Service {

    const PARENT_AUTH_KEY = 'yunj_set';

    /**
     * @var string
     */
    private $group;

    /**
     * @var array
     */
    private $groupConfig;

    /**
     * @return string
     */
    public function getGroup(): string {
        if (is_null($this->group)) {
            $this->setGroup();
        }
        return $this->group;
    }

    /**
     * @param string|null $group
     * @return SettingService
     */
    public function setGroup(?string $group = null): SettingService {
        $group = $group ?: request()->get('group', 'sys');
        $this->group = $group;
        $this->setGroupConfig();
        return $this;
    }

    /**
     * @param string $key
     * @param null $def
     * @return array|mixed|null
     */
    public function getGroupConfig(string $key = '', $def = null) {
        if (is_null($this->groupConfig)) {
            $this->setGroupConfig();
        }
        return $key ? ($this->groupConfig[$key] ?? $def) : $this->groupConfig;
    }

    /**
     * @param string|null $groupConfig
     * @return SettingService
     */
    public function setGroupConfig(): SettingService {
        $group = $this->getGroup();
        if (!$group) throw new \RuntimeException('页面错误，请刷新页面后重新进入');
        $allGroupsConfig = self::getAllGroupsConfig();
        $config = $allGroupsConfig[$group] ?? [];
        if (!$config) throw new \RuntimeException('页面参数错误，请刷新页面后重新进入');
        $this->groupConfig = $config;
        return $this;
    }

    // 获取所有的groups配置
    public static function getAllGroupsConfig(): array {
        $groups = Config::get('setting.groups', []) + yunj_config('setting.groups', []);
        return $groups;
    }

    /**
     * 权限key
     * @param string $group
     * @param string $sufix
     * @return string
     */
    public static function authKey(string $group = '', string ...$sufix): string {
        $key = self::PARENT_AUTH_KEY;
        if ($group) {
            $key .= '_' . $group;
        }
        foreach ($sufix as $v) {
            if ($v) {
                $key .= '_' . $v;
            }
        }
        return $key;
    }

    /**
     * 所有的权限
     * @return array[]
     */
    public static function auths() {
        $parentAuthKey = self::authKey();
        $auths = [
            $parentAuthKey => [
                'name' => '设置',
                'icon' => 'layui-icon-set',
                'parent' => 'yunj_system',
                'type' => AuthType::SIDEBAR_MENU,
            ],
        ];
        $groups = self::getAllGroupsConfig();
        foreach ($groups as $k => $group) {
            $authKey = self::authKey($k);
            $authName = $group['title'] ?? '';
            $authParent = $group['parent'] ?? $parentAuthKey;
            $authUrl = build_url('yunjSetting');
            // 侧边栏菜单
            $auths[$authKey] = [
                'name' => $authName,
                'icon' => $group['icon'] ?? '',
                'parent' => $parentAuthKey,
                'type' => AuthType::SIDEBAR_MENU,
                'page_open' => AuthPageOpen::TAB,
                'url' => [$authUrl, 'get', ['group' => $k]],
            ];
            // 表单
            if (!($groupForm = $group['form'] ?? [])) continue;
            // 字段权限
            if ($groupFormFieldAuths = $groupForm['field_auths'] ?? []) {
                $fieldAuthKey = $authKey . '_field';
                $auths[$fieldAuthKey] = [
                    'name' => '表单字段',
                    'parent' => $authKey,
                ];
                foreach ($groupFormFieldAuths as $kk => $auth) {
                    $parent = $auth['parent'] ?? $fieldAuthKey;
                    $auths[$kk] = [
                            'parent' => $parent
                        ] + $auth;
                }
            }
            if (isset($groupForm['field'])) {
                // 重置权限
                $auths[$authKey . '_reset'] = [
                    'name' => '表单重置',
                    'parent' => $authKey,
                ];
                // 提交
                $auths[$authKey . '_submit'] = [
                    'name' => '表单提交',
                    'parent' => $authKey,
                    'url' => [$authUrl, 'post', ['group' => $k, BuilderConst::ASYNC_TYPE_KEY => 'submit']]
                ];
            }
        }
        return $auths;
    }

    /**
     * 所有的路由请求项
     * @return array[]
     */
    public static function requestItems() {
        $requestItems = [];
        $groups = self::getAllGroupsConfig();
        foreach ($groups as $k => $group) {
            $requestItems = array_merge($requestItems, [
                [
                    'system_key' => md5('setting_page_' . $k),
                    'type' => 'request',
                    'desc' => $group['title'] . '页面',
                    'method' => RequestMethod::GET,
                    'require_params' => [
                        'group' => $k
                    ],
                ],
                [
                    'system_key' => md5('setting_submit_' . $k),
                    'type' => 'request',
                    'desc' => $group['title'] . '数据保存',
                    'method' => RequestMethod::POST,
                    'require_params' => [
                        'group' => $k,
                        BuilderConst::ASYNC_TYPE_KEY => 'submit'
                    ],
                ]
            ]);
        }
        return $requestItems;
    }

    /**
     * 获取表单构建器
     * @return YunjForm
     */
    public function getFormBuilder(): YunjForm {
        $group = $this->getGroup();
        $groupFormConfig = $this->getGroupConfig('form', []);

        $args = [];
        if ($tab = $groupFormConfig['tab'] ?? []) {
            $args['tab'] = $tab;
        }
        if ($groupFormConfig['field'] ?? []) {
            $args['field'] = function (YunjForm $builder, $tab) use ($groupFormConfig) {
                $field = call_user_func_array($groupFormConfig['field'], [$builder, $tab]);
                // default属性赋值
                $groupFieldDefaultConfig = $groupFormConfig['field_default'] ?? [];
                foreach ($field as $k => $v) {
                    if (array_key_exists($k, $groupFieldDefaultConfig)) {
                        $field[$k]['default'] = $groupFieldDefaultConfig[$k];
                    }
                }
                return $field;
            };
            $args['button'] = function () use ($group) {
                return [
                    'reset' => ['auth' => self::authKey($group, 'reset')],
                    'submit' => ['auth' => self::authKey($group, 'submit')],
                ];
            };
            $args['load'] = function () {
                return $this->formBuilderLoad();
            };
            $args['submit'] = function (YunjForm $builder, array $data) {
                return $this->formBuilderSubmit($builder, $data);
            };
            if ($groupFormConfig['validate'] ?? null) {
                $args['validate'] = $groupFormConfig['validate'];
            }
        }

        return YF('SettingForm', $args);
    }

    /**
     * 表单构建器load
     * @return array|string
     */
    private function formBuilderLoad() {
        $group = $this->getGroup();
        $datas = self::getSettingModel()->getColumnOptions('value', [['group', '=', $group]], [],[], 'key');

        // 刷新缓存
        /** @var RedisKey $settingValueByKeyObj */
        $settingValueByKeyObj = RedisKey::SETTING_VALUE_BY_KEY();
        foreach ($datas as $k => $v) {
            $settingValueByKeyObj->setArgs($group, $k);
            $settingValueByKeyObj->setCacheValue($v, self::getCacheTTL());
        }

        return $datas ?: [];
    }

    /**
     * 表单构建器submit
     * @param YunjForm $builder
     * @param array $data
     * @return \yunj\core\response\Json
     */
    private function formBuilderSubmit(YunjForm $builder, array $data) {
        try {
            $fieldValues = $builder->getSubmitFieldValues();
            if (!$fieldValues) {
                return success_json(["reload" => true]);
            }
            $group = $this->getGroup();
            $keys = array_keys($fieldValues);
            $rawSettings = self::getSettingModel()->getOwnRowsToArray([
                ['group', '=', $group],
                ['key', 'in', $keys],
            ], ['id', 'key', 'value']);
            if ($rawSettings) {
                $rawSettings = array_column($rawSettings, null, 'key');
            }
            // 新增和修改的值
            $addDatas = [];
            $editDatas = [];
            // 修改的key用于清理缓存
            $editKeys = [];
            $currTime = time();
            foreach ($fieldValues as $k => $v) {
                if ($rawSetting = $rawSettings[$k] ?? []) {
                    if ($v !== $rawSetting['value']) {
                        $editDatas[] = [
                            'id' => $rawSetting['id'],
                            'value' => $v,
                            'last_update_time' => $currTime,
                        ];
                        $editKeys[] = $k;
                    }
                } else {
                    $addDatas[] = [
                        'group' => $group,
                        'key' => $k,
                        'value' => $v,
                        'create_time' => $currTime,
                        'last_update_time' => $currTime,
                    ];
                }
            }
            // 触发设置保存前事件
            event('AdminSettingSaveBefore', ['group' => $group, 'groupConfig' => $this->getGroupConfig(), 'values' => $fieldValues]);
            if ($addDatas) {
                self::getSettingModel()->addRows($addDatas);
            }
            if ($editDatas) {
                self::getSettingModel()->batchChange($editDatas);
            }
            // 清除缓存
            /** @var RedisKey $settingValueByKeyObj */
            $settingValueByKeyObj = RedisKey::SETTING_VALUE_BY_KEY();
            foreach ($editKeys as $editKey) {
                $settingValueByKeyObj->setArgs($group, $editKey);
                $settingValueByKeyObj->delCacheValue();
            }
            return success_json();
        } catch (ResponseJsonException $e) {
            throw $e;
        } catch (\Exception $e) {
            log_exception($e);
            return error_json($this->getGroupConfig('name') . '修改失败');
        }
    }

    /**
     * 设置值获取
     * @param string $key
     * @return mixed|null
     */
    public function value(string $key) {
        /** @var RedisKey $settingValueByKeyObj */
        $settingValueByKeyObj = RedisKey::SETTING_VALUE_BY_KEY();
        $res = $settingValueByKeyObj->setArgs($this->getGroup(), $key)->getCacheValue(self::getCacheTTL(), true);
        if ($res !== false && $res !== null) {
            return $res;
        }
        // 获取字段默认值
        $groupFormConfig = $this->getGroupConfig('form');
        $fieldDefaultConfig = $groupFormConfig['field_default'] ?? [];
        if (array_key_exists($key, $fieldDefaultConfig)) {
            return $fieldDefaultConfig[$key];
        }
        return null;
    }

    // 获取缓存过期时间
    private static function getCacheTTL(): int {
        return rand(86400, 86400 * 2);
    }

}